/* ----------------------------------------------------------------------------

	pax.widget.datagrid.js Copyright (C) 2006, 2008 Mikkel Bergmann, Pointful

	Licence
	-------
	
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

	See lgpl.txt for licence details

---------------------------------------------------------------------------- */

/*
	Script: pax.widget.datagrid
		This is a datagrid widget, that can render in various ways
		
	Author:
		Mikkel Bergmann, <http://www.pointful.com>

*/

var pax = pax || {};
pax.widget = pax.widget || {};
pax.widget.datagrid = pax.widget.datagrid || {};					// The data grid
pax.widget.datagrid.filter = pax.widget.datagrid.filter || {};		// A set of pre defined filter templates

/*	Method: pax.widget.datagrid.init
	Initialises a grid for ajax loading
	
	Parameters:		
		target - an element to render the grid into
		args - arguments for the grid widget
		* url - Data loading URL
		* rowClick - A function we fire on row click
		* itemClick - A function we fire on item click
		* offset - Data index offset
		* limit - Data row limit
		* pageNumberLimit - Limit of how many pages to show before using dots, ie: "..."
		* sortDirection - The sort direction variable name sent to the server
		* reverseSortDirection - The reverse direction sort variable name sent to the server
		* ignoreCols - Columns to ignore in the display
		* applyCols - Templates to apply to items in a column
		* filterCols - Templates to apply to the filter header, this is work in progress
		* colName - Column name override
		* colWidth - Optional width of each column
		* colWidthDefault - Default column width
		* colAlign - Optional alignment of each column, use column name: [left, right, center]
		* width - Width of the grid - automatically grows if not specified, use pixels only
		* height - Height of the grid - if not specified, grid won't scroll, use pixels only
		* showHeader - If we want to show the header.
		* showFooter - If we want to show the footer.
		* showSearch - If we want to show the search box
		* data - The data in the grid

	Returns:
		nothing

	Example:
		(start code)
			<div id='widget.datagrid.init.example1'></div>
			[:.
				var args = {
					url: '/pax/documentation/pax.widget.example.table.php',
					limit: 10,
					ignoreCols: [ 'notes' ],
					colWidth: { id: '30px', name: '30%', country: '30%' },
					sort: 'name'
				};
				
				pax.widget.datagrid.init( pax.$('widget.datagrid.init.example1'), args );
			:]
		(end)
		This example sets up a simple ajax data grid, getting the data from the given php file.
		See the data grid demo for a live demo: <http://paxjs.com/demo_data_grid.htm>
*/
/*
	TODO: 
	. Add a template for row rendering, so it can easily be over ridden.
	. Make this use pax.data.js, so we can parse various formats easily. Allow passing in a formatter function.
	. Make templates overridable from the model
	
*/
pax.widget.datagrid.init = function( target, args ) {
	args = args || {};

	//	Setup the default config values for this widget (used internally for keeping state)
	var model = pax.defaultArgs( {
		target: target,							// Target for rendering the data grid
		url: '',								// Data loading URL
		rowClick: null,							// The function we fire on row click
		itemClick: null,						// The function we fire on item click
		offset: 0,								// Data index offset
		limit: 5,								// Data row limit
		pageNumberLimit: 8,						// Limit of how many pages to show before using ...
		sortDirection: 'SORT_ASC',				// The sort direction sent to the server
		reverseSortDirection: 'SORT_DESC',		// The reverse sort direction sent to the server
		ignoreCols: [],							// Columns to ignore in the display
		applyCols: {},							// Templates to apply to items in a column
		filterCols: {},							// Templates to apply to the filter header
		colName: {},							// Column name override
		colWidth: {},							// Optional width of each column
		colWidthDefault: 150,					// Default column width
		colAlign: {},							// Optional alignment of each column, use left, right, center
		width: 500,								// Width of the grid - automatically grows if not specified
		height: 200,							// Height of the grid - if not specified, grid won't scroll
		offsetScrollWidth: 17,					// Pixels to offset for the scrollbar on the right of the grid
		showHeader: true,						// If we want to show the header.
		showFooter: true,						// If we want to show the footer.
		showSearch: false,						// If we want to show the search box
		hasRendered: false,						// If the template has been rendered once.
		data: null,								// The data in the grid
		param: {								// Setup the default parameters (passed to the server on each request)
			offset: 0,							// Data index offset
			limit: 5,							// Data row limit
			sort: '',							// Column to sort by
			sortDirection: 'SORT_ASC',			// Sort direction, either SORT_ASC or SORT_DESC
			search: ''							// Keyword search
		},
		
		/*	retreives the name of a column, from the element clicked */
		getColumnName: function( target ) {
			var value = target.getAttribute( 'colName' );
			if( ! pax.util.hasClassName( target, 'headerContent' ) ) {
				value = pax.util.getElementsByClassName( target, '*', 'headerContent' )[0].getAttribute( 'colName' );
			}
			return value;
		},
		
		/* 	Below we have functions that will be bound to various parts of the widget.
			These can be overridden as the grid is initialised. */
			
		// These functions are bound by class name
		bindClasses: {
			firstButton: {
				click: function(ele) {
					grid.model.param.offset = 0;
					grid.serverRequest();
				} 
			},
			prevButton: {
				click: function(ele) {
					grid.model.param.offset = parseInt( grid.model.param.offset, 10 ) - parseInt( grid.model.param.limit );
					if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
					grid.serverRequest();
				} 
			},
			nextButton: {
				click: function(ele) {
					//	Whilst we do know the size of the data set, when someone clicks the 'next' button, we still check to see if the data set has grown
					grid.model.param.offset = parseInt( grid.model.param.offset, 10 ) + parseInt( grid.model.param.limit, 10 );
					if( typeof( response.offsetEnd ) != 'undefined' && grid.model.param.offset > (response.offsetEnd * grid.model.param.limit) ) {
						grid.model.param.offset = response.offsetEnd * grid.model.param.limit; 
					};
					grid.serverRequest();
				} 
			},
			lastButton: { 
				click: function(ele) {
					grid.model.param.offset = (response.pages - 2) * grid.model.param.limit;
					grid.serverRequest();
				} 
			},
			pGridHeader: {
				click: function(ele) {
					var target = ele.target || window.event.srcElement;
					var value = grid.model.getColumnName( target );
					
					/*	Set the sortDirection; it should be the default, if we're clicking on a different 
						header than the current sort, and swapped if not.	*/
					if( grid.model.param.sort == value ) {
						grid.model.param.sortDirection = ( grid.model.param.sortDirection == grid.model.sortDirection )? 
							grid.model.reverseSortDirection : 
							grid.model.sortDirection;
					} else grid.model.param.sortDirection = grid.model.sortDirection;
					grid.model.param.sort = value;
					grid.serverRequest();
				},
				mouseover: function(ele) {
					var target = ele.target || window.event.srcElement;
					target = ( pax.util.hasClassName( target.parentNode, 'pGridTD' ) )? target.parentNode: target; 
					pax.util.addClassName( target, 'headerRowHover' );
				},
				mouseout: function(ele) {
					var target = ele.target || window.event.srcElement;
					target = ( pax.util.hasClassName( target.parentNode, 'pGridTD' ) )? target.parentNode: target; 
					pax.util.removeClassName( target, 'headerRowHover' );
				}
			}
		},
		
		// These functions are bound by event type, then class name.
		bindOneClasses: {
			pageNumber: {
				click: function(e) {			// Clicking on a page number
					grid.model.param.offset = ( parseInt(this.innerHTML) - 1 ) * grid.model.param.limit;
					if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
					grid.serverRequest();
				},
				mouseover: function(e)	{ pax.util.addClassName( this, 'hoverPage'); },
				mouseout: function(e)	{ pax.util.removeClassName( this, 'hoverPage'); }
			},
			pGridRow: {
				click: function( e, target ) {
					//	The row number is the number after the last _ in the id. id='[:= theGrid.model.target.id + '_row_' + i :]'
					var rn = target.id;
					rn = rn.substring( rn.lastIndexOf( '_' ) + 1, 99 );
					//	Return the data row, target and event
					if( grid.model.rowClick )grid.model.rowClick( { 
						row: rn,
						data: grid.model.data[rn], 
						target: target, 
						event: e 
					} );
				}
			},
			item: {
				click: function( e, target ) {
					//	The row number is the number after the last _ in the id. id='[:= theGrid.target.id :]_[:= name :]_[:= i :]'
					var rn = target.id;
					rn = rn.substring( rn.lastIndexOf( '_' ) + 1, 1000 );
					//	The column name is 2nd last
					var col = target.id;
					col = col.substring( 0, col.lastIndexOf( '_' ) );
					col = col.substring( col.lastIndexOf( '_' ) + 1, 1000 );
					
					var target = pax.util.getTarget( e );
					if( grid.model.itemClick )grid.model.itemClick( {
						row: rn,
						col: col,
						data: grid.model.data[rn][col],
						target: target,
						event: e
					} );
				},
				mouseout: function(e) {
					pax.util.removeClassName( this, 'itemHover' ); 
					var pEle = this.parentNode;	// Each item is contained in a dataRow, which is what we want to change the hover over
					pEle = pEle.parentNode;
					pax.util.removeClassName( pEle, (pax.util.hasClassName(pEle, 'r0Hover')? 'r0Hover' : 'r1Hover' ) ); 
				},
				mouseover: function(e) {
					pax.util.addClassName( this, 'itemHover' ); 
					var pEle = this.parentNode;	// Each item is contained in a dataRow, which is what we want to change the hover over
					pEle = pEle.parentNode;
					pax.util.addClassName( pEle, (pax.util.hasClassName(pEle, 'r0')? 'r0Hover' : 'r1Hover' ) ); 
				}
			}
		},
		
		// Bind IDs post-fixed with '_' + model.target.id
		internalBindIds: {
			search: {
				keydown: function( e ) {
					if( e.keyCode == 13 ) {
						grid.model.param.search = this.value;
						grid.serverRequest();
					}
				}
			},
			goSearch: {
				click: function() {
					grid.model.param.search = pax.$('search_' + grid.target.id ).value;
					grid.serverRequest();
				}
			},
			offsetBox: {
				keydown: function( e ) {
					if( e.keyCode == 13 ) {
						grid.model.param.offset = ( parseInt(this.value) - 1 ) * grid.model.param.limit;
						if( grid.model.param.offset < 0 )grid.model.param.offset = 0;
						grid.serverRequest();
					}
				}
			}
		}
	}, args );

	//	Setup controller: it receives the ajax response, renders the template, and sets up event handeling
	var gridController = function( xml, txt, url ) {	// This is the default callback handle function
		var response = pax.unJSON( txt );
		
		//	Re-render the widget if we have a response
		if( response && response != '' ) {
			//	Save the response data
			if( response.data )grid.model.data = response.data;
		
			// Check if the server response was at the end of the result set, and set the offset accordingly
			if( typeof( response.offsetEnd ) != 'undefined' )grid.model.param.offset = response.offsetEnd * grid.model.param.limit;
			
			if( pax.$('sort_' + grid.target.id) )pax.box.hide( 'sort_' + grid.target.id );	// Hide the sort direction image
			
			//	Get tags with 'headerContent' class to link to the server side sorting.
			var myHeaders = pax.util.getElementsByClassName( grid.target, '*', 'headerContent' );
			
			for( var h = 0; h < myHeaders.length; h++ ) {
				var myHeader = myHeaders[h];
				pax.util.removeClassName( myHeader, 'sortHeaderOn' );
				pax.util.removeClassName( myHeader, 'sortHeaderOnAsc' );
				pax.util.removeClassName( myHeader, 'sortHeaderOff' );
				
				var value = grid.model.getColumnName( myHeader );
				
				//	Find out which class we should add
				var classToAdd = ( grid.model.param.sort != value )? 
					'sortHeaderOff': ( grid.model.param.sortDirection == grid.model.sortDirection )?
					'sortHeaderOnAsc' : 
					'sortHeaderOn';
					
				pax.util.addClassName( myHeader, classToAdd );
			};
		
			if( grid.model.hasRendered ) {	// Render the partial view
				pax.template.render( pax.widget.datagrid.template( grid ), {
					value: { response: response, theGrid: grid }, 
					partial: 'DataGridPartial_' + grid.model.target.id
				} );
				//	Update footer offset
				pax.$( 'offsetBox_' + grid.model.target.id ).value = ( grid.model.param.offset / grid.model.param.limit ) + 1;
			} else {
				grid.model.hasRendered = true;
				pax.widget.datagrid.destroy( grid );	//	Destroy any old widget, if necessary

				// Calculate width of the grid in pixels. Note: percent won't work!
				var myWidth = 0;
				for( var name in response.data[0] ) {
					if( !pax.util.hasValue( name, grid.model.ignoreCols ) ) {
						myWidth += parseInt(
							( pax.util.hasKey( name, grid.model.colWidth ) )? 
								grid.model.colWidth[name]: 
								grid.model.colWidthDefault
							);
					};
				};
				
				grid.model.width = myWidth;

				// Render the view
				pax.template.render( pax.widget.datagrid.template( grid ), {
					value: { response: response, theGrid: grid }, 
					target: target 
				} );
				
				grid.bindOneEvent( grid.target, grid.model.bindOneClasses );		// Bind just one function for each event type, as we have a lot of items / page numbers.
				grid.bindClassNames( grid.target, grid.model.bindClasses );			// Bind specific class names 
				grid.internalBindIds( grid.target, grid.model.internalBindIds );	// Bind specific IDs, automatically post-fixed with '_' + grid.model.target.id
			}
		}
	};

	var grid = pax.widget.ajax.init( model.url, {
		model: model, 
		controller: gridController, 
		target: target 
	} );
	
	grid.serverRequest();
};


pax.widget.datagrid.destroy = function( model ) {
	model._destroy();	
};


/*	Private method: pax.widget.datagrid.rowTemplate
	Returns template that is rendered for each row
	
	Note: 	if you're going to change this, make sure you name elements EXACTLY
			THE SAME, as they are referred to in various events.

*/
pax.widget.datagrid.rowTemplate = function( grid ) {
	var tpl = "" +
		"					<table cellpadding='0' cellspacing='0'>" +
		"						<tr>" +
		"							[:" +
		"								for( var name in response.data[i] ) {" +
		"									if( !pax.util.hasValue( name, theGrid.model.ignoreCols ) ) {" +
		"										var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"										var colWidthStyle = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"							:]" +
		"							<td class='pGridTD' [:= colWidthStyle :] align='[:= ( pax.util.hasKey(name, theGrid.model.colAlign) )? theGrid.model.colAlign[name]: 'left' :]'>" +
		"								<div style='width: [:= colWidth - 9 :]px' class='pGridCell item content' id='[:= theGrid.target.id :]_[:= name :]_[:= i :]' unselectable='on'>"+
		"									[: if( pax.util.hasKey( name, theGrid.model.applyCols ) ) { :]" +
		"										[:= push( theGrid.model.applyCols[name], { value: response.data[i][name], rowTarget: pax.getNextId() } ) :]" +
		"									[: } else { :]" + 
		"										[:= response.data[i][name] :]" +
		"									[: } :]" +
		"								</div>" +
		"							</td>" +
		"							[:" +
		"									}" +
		"								}" +
		"							:]" +
		"						</tr>" +
		"					</table>";
	return tpl;
};


/*	Method: pax.widget.datagrid.template
	Returns a PAX template for the datagrid
*/
pax.widget.datagrid.template = function( grid ) {
	var myTemplate = "" +
		"<div class='pGrid' style='width: [:= theGrid.model.width + theGrid.model.offsetScrollWidth :]px'>";

	//	Add Header and filters, if needed.
	if( grid.model.showHeader )myTemplate += pax.widget.datagrid.templateHeader();
	if( pax.util.numItems( grid.model.filterCols ) > 0 )myTemplate += pax.widget.datagrid.templateFilter();
	
	myTemplate += "" +
		"	<div class='pGridBody container'>" +
		"		<div class='pGridScroll' style='height: [:= theGrid.model.height :]px'>" +
		"			<div style='width: [:= theGrid.model.width :]px'>" +
		"				[:p(DataGridPartial_[:= theGrid.model.target.id :])" +		//	Partial rendering
		"				<span>" +
		"				[: for( var i = 0; i < response.data.length; i++ ) { :]" +
		"				<div class='pGridRow r[:= i % 2 :]' id='[:= theGrid.model.target.id + '_row_' + i :]'>" +
		"";
		
	myTemplate += pax.widget.datagrid.rowTemplate();
		
	myTemplate += "" +
		"				</div>" +
		"				[: } :]" +
		"				</span>" +
		"				p:]" +		//	End partial rendering
		"			</div>			" +
		"		</div>" +
		"	</div>" +
		"	";

	//	Add footer
	if( grid.model.showFooter )myTemplate += pax.widget.datagrid.templateFooter();

	myTemplate += "" +
		"</div>" +
	"";
	return myTemplate;
};

/*
	Header of data grid
*/
pax.widget.datagrid.templateHeader = function() {
	var myHeaderTemplate = "" +
		"	<div class='pGridHeader'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					[:	for( var name in response.data[0] ) { " +
		"							if( !pax.util.hasValue( name, theGrid.model.ignoreCols) ) {" +
		"								var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"								colWidth = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"					:]" +
		"					<td class='pGridTD' [:= colWidth :]>" +
		"						[: if( !pax.isIe )colWidth = '' :]" +
		"						<div class='pGridCell header headerContent' unselectable='on' colName='[:= name :]'>[:= _caps( ( pax.util.hasKey( name, theGrid.model.colName ) )? theGrid.model.colName[name]: name ) :]</div>" +
		"					</td>" +
		"					[:		}" +
		"						}" +
		"					:]" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
	"";
	return myHeaderTemplate;
};

/*
	Filter section of data grid
*/
pax.widget.datagrid.templateFilter = function() {
	var myTemplate = "" +
		"	<div class='pGridFilter'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					[: for( var name in response.data[0] ) { " +
		"							if( !pax.util.hasValue( name, theGrid.model.ignoreCols) ) {" +
		"								var colWidth = ( pax.util.hasKey(name, theGrid.model.colWidth) )? theGrid.model.colWidth[name]: theGrid.model.colWidthDefault;" +
		"								colWidth = 'style=\\'width: ' + colWidth + 'px\\''; " +
		"					:]" +
		"					<td class='pGridTD' [:= colWidth :]>" +
		"						[: if( !pax.isIe )colWidth = ''; :]" +
		"						<div class='pGridCell header headerContent'>" +
		"							[: if( pax.util.hasKey( name, theGrid.model.filterCols ) ) { :]" +
		"								[:= push( theGrid.model.filterCols[name], { value: {}, rowTarget: pax.getNextId() } ) :]" +
		"							[: } :]" +
		"						</div>" +
		"					</td>" +
		"					[:		}" +
		"						}" +
		"					:]" +
		"				</tr>" +
		"			</table>" +
		"		</div>		" +
		"	</div>" +
	"";
	return myTemplate;
};

/*
	Footer of data grid
*/
pax.widget.datagrid.templateFooter = function() {

	//	baseClass

	var myTemplate = "" +
		"	<div class='pGridFooter'>" +
		"		<div style='width: [:= theGrid.model.width :]px'>" +
		"			<table cellpadding='0' cellspacing='0'>" +
		"				<tr>" +
		"					<td class='pGridTD'>" +
		"						<div class='pGridCell footer'>" +
		"							<table cellpadding='0' cellspacing='1' border='0'><tr>" +
		"								<td style='width: 30px'><span class='firstButton' id='firstButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px'><span class='prevButton' id='prevButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px' class='pGridTD'>&nbsp;Page <input size='3' style='width: 30px' id='offsetBox_[:= theGrid.target.id :]' value='[:= ( theGrid.model.param.offset / theGrid.model.param.limit ) + 1 :]'> of <span id='numPages'>[:= response.pages - 1 :]</span>&nbsp;</td>" +
		"								<td style='width: 30px'><span class='nextButton' id='nextButton_[:= theGrid.target.id :]'></span></td>" +
		"								<td style='width: 30px'><span class='lastButton' id='lastButton_[:= theGrid.target.id :]'></span></td>" +
		" 								[:." +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('firstButton_' + theGrid.target.id), {  "+
		"										buttonClass: 'arrowLeftFull', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('prevButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowLeft', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('nextButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowRight', " +
		"										width: '20px' } ) " +
		"									);" +
		"									theGrid.useWidget(	pax.widget.button.init( pax.$('lastButton_' + theGrid.target.id),	 { " +
		"										buttonClass: 'arrowRightFull', " +
		"										width: '20px' } ) " +
		"									);" +
		"								:]" +		
		"								[: if( theGrid.model.showSearch ) { :]" +
		"								<td><span id='searchBox'><input type='text' id='search_[:= theGrid.target.id :]'><button id='goSearch_[:= theGrid.target.id :]'>Go</button></span></td>" +
		"								[: } :]" +
		"								<td><span id='StatusBox'></span></td>" +
		"							</tr></table>" +
		"						</div>" +
		"					</td>" +
		"				</tr>" +
		"			</table>" +
		"		</div>" +
		"	</div>" +
	"";
	return myTemplate;
};


/*	Private Method: pax.widget.datagrid.filter.alpha
	Returns a template with 2 alpha dropdowns, to be able to set a range. You can add your own, or override these, for custom filter headers
	
	Parameters:
		id - An element id to refer to the alpha dropdowns by.
		Note: The id will be pre and post fixed, so if you pass 'name' as the id, you will get two selects, with ids like this: alpha_name_1 and alpha_name_2

	Returns:
		Template string

	Example:
		(start code)
			<div id='widget.datagrid.fliter.alpha.example1'></div>
			[:.
				pax.template.render( pax.widget.datagrid.filter.alpha( 'name' ), { 
					target: pax.$('widget.datagrid.fliter.alpha.example1')
				} );
			:]
		(end)
		This example sets up a alpha range selector.
*/
pax.widget.datagrid.filter.alpha = function( id ) {
	var myTemplate = "" +
		"[:_ if( ! value.alpha_" + id + "_2 ) { value.alpha_" + id + "_2 = 'z' } :]" +
		"<select id='alpha_" + id + "_1'>[: for( var x = 97; x < 123; x++ ) { ch = String.fromCharCode(x); :]<option [:= (ch == value.alpha_" + id + "_1)? 'selected': '' :]>[:= ch :]</option>[: } :]</select> - " +
		"<select id='alpha_" + id + "_2'>[: for( var x = 97; x < 123; x++ ) { ch = String.fromCharCode(x); :]<option [:= (ch == value.alpha_" + id + "_2)? 'selected': '' :]>[:= ch :]</option>[: } :]</select>" +
	"";	
	
	return myTemplate;
};


/*	Private Method: pax.widget.datagrid.filter.numeric
	Returns a template with 2 numeric dropdowns, to be able to set a range. You can add your own, or override these, for custom filter headers
	
	Parameters:
		id - An element id to refer to the numeric dropdowns by.
		start - starting value, eg: 10
		end - end value, eg: 1000
		Note: The id will be pre and post fixed, so if you pass 'name' as the id, you will get two selects, with ids like this: alpha_name_1 and alpha_name_2

	Returns:
		Template string

	Example:
		(start code)
			<div id='widget.datagrid.fliter.numeric.example1'></div>
			[:.
				pax.template.render( pax.widget.datagrid.filter.numeric( 'num', 0, 10 ), { 
					target: pax.$('widget.datagrid.fliter.numeric.example1')
				} );
			:]
		(end)
		This example sets up a numeric range selector.
*/
pax.widget.datagrid.filter.numeric = function( id, start, end ) {
	start = ( start )? start: 1;
	end = ( end )? end: 11;
	var myTemplate = "" +
		"[:_ if( ! value.numeric_" + id + "_2 ) { value.numeric_" + id + "_2 = 0 } :]" +
		"<select id='numeric_" + id + "_1'>[: for( var x = "+ start +"; x < "+ end +"; x++ ) { :]<option [:= (x == value.numeric_" + id + "_1)? 'selected': '' :]>[:= x :]</option>[: } :]</select> - " +
		"<select id='numeric_" + id + "_2'>[: for( var x = "+ start +"; x < "+ end +"; x++ ) { :]<option [:= (x == value.numeric_" + id + "_2)? 'selected': '' :]>[:= x :]</option>[: } :]</select>" +
	"";
	
	return myTemplate;
};